package attendance

import (
	"errors"
	"io"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"gitee.com/baal1990/gitee.com/baal1990/cmd/server_attence/app/business/holiday"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server_attence/app/config"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server_attence/app/dao"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server_attence/app/entity"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server_attence/app/global"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/file"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/model"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/util"
	"github.com/360EntSecGroup-Skylar/excelize"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"gopkg.in/gomail.v2"
)

func CalcAttendanceInfo(c *gin.Context) {
	user := c.MustGet("USER").(*entity.Employee)
	is_valid := CheckValidity(user)
	if !is_valid {
		global.Log.Error("CalcAttendanceInfo invalid user role", zap.Int("role", user.Role))
		c.JSON(http.StatusOK, model.Fail("用户角色无权限执行该操作"))
		return
	}
	start_date_str := strings.TrimSpace(c.PostForm("startDate"))
	end_date_str := strings.TrimSpace(c.PostForm("endDate"))
	start_date, err := time.Parse(entity.DateFormat, start_date_str)
	if err != nil {
		global.Log.Error("CalcAttendanceInfo invalid start date", zap.String("start date", start_date_str))
		c.JSON(http.StatusOK, model.Fail("起始时间错误"+start_date_str))
		return
	}
	end_date, err := time.Parse(entity.DateFormat, end_date_str)
	if err != nil {
		global.Log.Error("CalcAttendanceInfo invalid end date", zap.String("end date", end_date_str))
		c.JSON(http.StatusOK, model.Fail("结束时间错误"+end_date_str))
		return
	}

	file_name, err := RecvExeclFile(c)
	if err != nil {
		global.Log.Error("CalcAttendanceInfo Recv Excel File Failed", zap.String("reason", err.Error()))
		c.JSON(http.StatusOK, model.Fail("上传文件错误："+err.Error()))
		return
	}
	global.Log.Info("ImportExcel create excel file success.", zap.Any("filename", file_name))

	employee_map, id_employee_map, err := GetEmployeeInfo()
	if err != nil {
		global.Log.Error("CalcAttendanceInfo Query Employee Failed", zap.String("reason", err.Error()))
		c.JSON(http.StatusOK, model.Fail("获取员工信息失败"))
		return
	}
	department_map, err := GetDepartmentInfo()
	if err != nil {
		global.Log.Error("CalcAttendanceInfo Query Department Failed", zap.String("reason", err.Error()))
		c.JSON(http.StatusOK, model.Fail("获取部门信息失败"))
		return
	}
	attend_info_map, err := ParseCheckData(file_name, start_date, end_date, employee_map)
	if err != nil {
		global.Log.Error("CalcAttendanceInfo Parse Data Failed", zap.String("reason", err.Error()))
		c.JSON(http.StatusOK, model.Fail("解析数据失败"))
		return
	}
	err = GetApplicationInfo(start_date_str, end_date_str, attend_info_map)
	if err != nil {
		global.Log.Error("CalcAttendanceInfo Get Application Data Failed", zap.String("reason", err.Error()))
		c.JSON(http.StatusOK, model.Fail("获取加班休假数据失败"))
		return
	}
	attend_data_map := StatAttendanceInfo(attend_info_map, id_employee_map)
	out_file_name, err := WriteDataToExcel(start_date_str, end_date_str, attend_data_map, id_employee_map, department_map)
	if err != nil {
		global.Log.Error("CalcAttendanceInfo Generate Xml File Failed", zap.String("reason", err.Error()))
		c.JSON(http.StatusOK, model.Fail("生成Excel文件失败"))
		return
	}
	err = SendEMail(user.Email, out_file_name, start_date_str, end_date_str)
	if err != nil {
		global.Log.Error("CalcAttendanceInfo Send Email Failed", zap.String("reason", err.Error()))
		c.JSON(http.StatusOK, model.Fail("发送邮件失败"))
		return
	}
	c.JSON(http.StatusOK, model.Success("已发送至"+user.Email+", 请查收"))
}

func CheckValidity(user *entity.Employee) bool {
	if user.Role != 2 {
		return false
	}
	return true
}

func RecvExeclFile(c *gin.Context) (string, error) {
	form_file, header, err := c.Request.FormFile("excelFile") //文件
	if err != nil {
		global.Log.Error("upload file failed.", zap.Any("error", err))
		return "", err
	}
	file_name := header.Filename
	ext := path.Ext(file_name) //扩展名
	allow := ".xlsx"
	if strings.Index(allow, strings.ToLower(ext)) < 0 {
		global.Log.Info("ImportExcel file format error.", zap.Any("ext", file_name))
		return "", err
	}

	random_str := string(util.RandomCreateBytes(10))
	upload_dir := config.Server().UploadDir
	file_name = path.Join(upload_dir, "attendance_"+random_str+path.Ext(file_name))
	out, err := file.CreateFile(file_name) //file 在git环境中需要改包路径
	defer out.Close()
	if err != nil {
		global.Log.Error("ImportExcel create file error.", zap.Any("filename", file_name), zap.Any("error", err))
		return "", err
	}

	_, err = io.Copy(out, form_file)
	if err != nil {
		global.Log.Error("ImportExcel io.Copy error.", zap.Any("error", err))
		return "", err
	}
	return file_name, nil
}

func ParseCheckData(file_name string, start_date time.Time, end_date time.Time, employee_map map[string]*entity.Employee) (map[int64][]*entity.AttendanceInfo, error) {
	xlsx_file, err := excelize.OpenFile(file_name)
	global.Log.Info("open file " + file_name)
	if err != nil {
		return nil, err
	}
	day_gap := int((end_date.Unix()-start_date.Unix())/86400) + 1
	sheet_name := xlsx_file.GetSheetName(1)
	rows := xlsx_file.GetRows(sheet_name)
	if len(rows) <= 1 {
		return nil, errors.New("Empty Data!")
	}
	err = CheckFileValidity(rows[0])
	if err != nil {
		return nil, err
	}
	attend_info_map := make(map[int64][]*entity.AttendanceInfo)
	for mobile, employee_info := range employee_map {
		id := employee_info.Id
		if _, ok := attend_info_map[id]; !ok {
			InitAllAttendInfo(mobile, id, attend_info_map, start_date, day_gap)
		}
	}
	for i := 1; i < len(rows); i++ {
		if len(rows[i]) < 7 {
			continue
		}
		mobile := rows[i][entity.ECheckIn_Mobile]
		employee_info, ok := employee_map[mobile]
		if !ok {
			global.Log.Error("ParseCheckData No mobile in Employee", zap.String("mobile", mobile))
			continue
		}
		id := employee_info.Id
		checkin_date, err := time.Parse(entity.DateFormat, rows[i][entity.ECheckIn_Date])
		if err != nil {
			global.Log.Error("ParseCheckData invalid date", zap.String("mobile", rows[i][entity.ECheckIn_Mobile]), zap.String("Date", rows[i][entity.ECheckIn_Date]))
			continue
		}
		day_diff := int((checkin_date.Unix() - start_date.Unix()) / 86400)
		if day_diff >= day_gap || day_diff < 0 {
			continue
		}
		start_time, err := ConvertTimeToSecond(rows[i][entity.ECheckIn_Time])
		if err != nil {
			global.Log.Error("ParseCheckData invalid time", zap.String("mobile", rows[i][entity.ECheckIn_Mobile]),
				zap.String("Date", rows[i][entity.ECheckIn_Date]), zap.String("Time", rows[i][entity.ECheckIn_Time]))
			continue
		}
		size := len(rows[i])
		var tmp_end_time string
		for idx := size - 1; idx >= 0; idx-- {
			if rows[i][idx] != "" {
				tmp_end_time = rows[i][idx]
				break
			}
		}
		end_time, err := ConvertTimeToSecond(tmp_end_time)
		if err != nil {
			global.Log.Error("ParseCheckData invalid time", zap.String("mobile", rows[i][entity.ECheckIn_Mobile]),
				zap.String("Date", rows[i][entity.ECheckIn_Date]), zap.String("Time", rows[i][size-1]))
			continue
		}
		attend_info_map[id][day_diff].StartTime = start_time
		attend_info_map[id][day_diff].EndTime = end_time
		info := attend_info_map[id][day_diff]
		info.IsLackData = false
		valid_start_time, err := ConvertTimeToSecond(employee_info.WorkStart)
		if err != nil {
			global.Log.Error("ParseCheckData invalid Employee WorkStart", zap.String("reason", err.Error()))
		}
		valid_start_time += 59
		valid_end_time, err := ConvertTimeToSecond(employee_info.WorkEnd)
		if err != nil {
			global.Log.Error("ParseCheckData invalid Employee WorkEnd", zap.String("reason", err.Error()))
		}
		valid_start_time_am := valid_start_time
		valid_end_time_pm := valid_end_time

		late_time := valid_start_time + 1800
		late_time_am := late_time
		late_time_pm := entity.ValidStartTimePM + 1800
		leave_early_time := valid_end_time - 1800
		leave_early_time_am := entity.ValidEndTimeAM - 1800
		leave_early_time_pm := leave_early_time

		if info.IsWorkDay {
			if info.StartTime <= valid_start_time && info.EndTime >= valid_end_time {
				info.IsNormal = true
			}
			if info.StartTime <= valid_start_time_am && info.EndTime >= entity.ValidEndTimeAM {
				info.IsNormalAM = true
			}
			if info.StartTime <= entity.ValidStartTimePM && info.EndTime >= valid_end_time_pm {
				info.IsNormalPM = true
			}

			if info.StartTime > valid_start_time && info.StartTime <= late_time {
				info.IsLate = true
			}
			if info.StartTime > valid_start_time_am && info.StartTime <= late_time_am {
				info.IsLateAM = true
			}
			if info.StartTime > entity.ValidStartTimePM && info.StartTime <= late_time_pm {
				info.IsLatePM = true
			}

			if info.StartTime > late_time || info.EndTime < leave_early_time {
				info.IsAbsent = true
			}
			if info.StartTime > late_time_am || info.EndTime < leave_early_time_am {
				info.IsAbsentAM = true
			}
			if info.StartTime > late_time_pm || info.EndTime < leave_early_time_pm {
				info.IsAbsentPM = true
			}

			if info.EndTime < valid_end_time && info.EndTime >= leave_early_time {
				info.IsLeaveEarly = true
			}
			if info.EndTime < entity.ValidEndTimeAM && info.EndTime >= leave_early_time_am {
				info.IsLeaveEarlyAM = true
			}
			if info.EndTime < valid_end_time_pm && info.EndTime >= leave_early_time_pm {
				info.IsLeaveEarlyPM = true
			}
		} else {
			overtime := end_time - start_time
			if overtime >= 12600 {
				count := overtime / 1800
				info.OvertimeHour = float64(count) * 0.5
			}
		}
	}
	tmp_err := os.Remove(file_name)
	if tmp_err != nil {
		global.Log.Error("Del File "+file_name+" failed,error:=", zap.Any("error", tmp_err))
	}
	return attend_info_map, nil
}

func CheckFileValidity(row []string) error {
	if len(row) < 6 {
		return errors.New("数据列错误，应为6列，实际为" + strconv.Itoa(len(row)))
	}
	if row[0] != "员工工号" {
		return errors.New("第一列应为员工工号，实际为" + row[0])
	}
	if row[1] != "姓名" {
		return errors.New("第二列应为姓名，实际为" + row[1])
	}
	if row[2] != "公司" {
		return errors.New("第三列应为公司，实际为" + row[2])
	}
	if row[3] != "部门" {
		return errors.New("第四列应为部门，实际为" + row[3])
	}
	if row[4] != "考勤日期" {
		return errors.New("第五列应为考勤日期，实际为" + row[4])
	}
	if row[5] != "考勤时间" {
		return errors.New("第六列应为考勤时间，实际为" + row[5])
	}
	return nil
}

func InitAllAttendInfo(mobile string, id int64, attend_info_map map[int64][]*entity.AttendanceInfo,
	start_date time.Time, gap int) {
	attend_info_map[id] = make([]*entity.AttendanceInfo, gap)
	for i := 0; i < gap; i++ {
		curr_date := start_date.AddDate(0, 0, i)
		info := new(entity.AttendanceInfo)
		//info.Mobile = mobile
		info.DayDiff = i
		info.OvertimeHour = 0.0
		info.IsWorkDay = holiday.IsWorkDay(curr_date)
		info.IsNormal = !info.IsWorkDay
		info.IsNormalAM = info.IsNormal
		info.IsNormalPM = info.IsNormal
		info.IsLate = false
		info.IsLeaveEarly = false
		info.IsVacation = false
		info.IsVacationAM = false
		info.IsVacationPM = false
		info.IsAbsent = false
		info.IsLackData = info.IsWorkDay
		attend_info_map[id][i] = info
	}
}

func ConvertTimeToSecond(time_info string) (int, error) {
	items := strings.Split(time_info, ":")
	factor := 3600
	var seconds int = 0
	for _, item := range items {
		val, err := strconv.ParseInt(item, 10, 32)
		if err != nil {
			return -1, err
		}
		seconds += int(val) * factor
		factor /= 60
	}
	return seconds, nil
}

func GetEmployeeInfo() (map[string]*entity.Employee, map[int64]*entity.Employee, error) {
	var deptIds []int64
	count, employee_list, err := dao.QueryEmployeeList(0, 0, "", deptIds, 0, 0)
	if err != nil {
		return nil, nil, err
	}
	employee_map := make(map[string]*entity.Employee, count)
	id_employee_map := make(map[int64]*entity.Employee, count)
	for _, info := range employee_list {
		employee_map[info.Mobile] = info
		id_employee_map[info.Id] = info
	}
	return employee_map, id_employee_map, nil
}

func GetDepartmentInfo() (map[int64]*entity.Department, error) {
	count, department_list, err := dao.QueryDepartmentList(0, 0)
	if err != nil {
		return nil, err
	}
	deparment_map := make(map[int64]*entity.Department, count)
	for _, info := range department_list {
		deparment_map[info.DeptId] = info
	}
	return deparment_map, nil
}

func GetApplicationInfo(start_date, end_date string, attend_map map[int64][]*entity.AttendanceInfo) error {
	start_time, _ := time.ParseInLocation("2006-01-02 15:04:05", start_date+" 00:00:00", time.Local)
	end_time, _ := time.ParseInLocation("2006-01-02 15:04:05", end_date+" 23:59:59", time.Local)
	var appIds []int64
	var depIds []int64
	_, app_list, err := dao.QueryApplicationListForAttendance(0, 0, 0, 6, 0, start_date+" 00:00:00",
		end_date+" 23:59:59", appIds, depIds)
	if err != nil {
		return err
	}
	for _, info := range app_list {
		start_ap_date := time.Date(info.StartDate.Year(), info.StartDate.Month(), info.StartDate.Day(), 0, 0, 0, 0, info.StartDate.Location())
		end_ap_date := time.Date(info.EndDate.Year(), info.EndDate.Month(), info.EndDate.Day(), 0, 0, 0, 0, info.EndDate.Location())
		start_ap_time := info.StartDate
		end_ap_time := info.EndDate
		id := info.ApplicantId
		if _, ok := attend_map[id]; !ok {
			continue
		}
		if start_ap_time.After(end_ap_time) {
			continue
		}
		if start_ap_time.Before(start_time) {
			start_ap_time = start_time
			start_ap_date = time.Date(start_ap_time.Year(), start_ap_time.Month(), start_ap_time.Day(), 0, 0, 0, 0, start_ap_time.Location())
		}
		if end_ap_time.After(end_time) {
			end_ap_time = end_time
			end_ap_date = time.Date(end_ap_time.Year(), end_ap_time.Month(), end_ap_time.Day(), 0, 0, 0, 0, end_ap_time.Location())
		}
		if info.ApplicationType == entity.EApplicationType_Overtime {
			curr_date := start_ap_date
			for {
				if curr_date.After(end_ap_date) {
					break
				}
				idx := int((curr_date.Unix() - start_time.Unix()) / 86400)
				attend_map[id][idx].IsOverTime = true
				curr_date = curr_date.AddDate(0, 0, 1)
			}

		} else if info.ApplicationType == entity.EApplicationType_Vacation {
			idx_start := int((start_ap_date.Unix() - start_time.Unix()) / 86400)
			idx_end := int((end_ap_date.Unix() - start_time.Unix()) / 86400)
			for i := idx_start + 1; i <= idx_end-1; i++ {
				attend_map[id][i].IsVacation = true
			}
			if idx_end == idx_start {
				if start_ap_time.Hour() <= 8 && end_ap_time.Hour() >= 17 {
					attend_map[id][idx_start].IsVacation = true
				}
				if start_ap_time.Hour() <= 8 && end_ap_time.Hour() == 12 {
					attend_map[id][idx_start].IsVacationAM = true
				}
				if start_ap_time.Hour() >= 13 && end_ap_time.Hour() >= 17 {
					attend_map[id][idx_start].IsVacationPM = true
				}
			} else {
				if start_ap_time.Hour() <= 8 {
					attend_map[id][idx_start].IsVacation = true
				} else {
					attend_map[id][idx_start].IsVacationPM = true
				}

				if end_ap_time.Hour() >= 17 {
					attend_map[id][idx_end].IsVacation = true

				} else {
					attend_map[id][idx_end].IsVacationAM = true
				}
			}
		}
	}
	return nil
}

func StatAttendanceInfo(attend_map map[int64][]*entity.AttendanceInfo, id_employee_map map[int64]*entity.Employee) map[int64]*entity.AttendanceData {
	size := len(attend_map)
	attend_info_map := make(map[int64]*entity.AttendanceData, size)
	for id, attend_infos := range attend_map {
		employee_info, ok := id_employee_map[id]
		if !ok {
			continue
		}
		name := employee_info.Name
		data := new(entity.AttendanceData)
		data.Name = name
		data.Mobile = employee_info.Mobile
		for _, info := range attend_infos {
			if info.IsWorkDay {
				data.WorkDays += 1
			}
			if info.IsLackData && !info.IsVacation {
				data.LackTimes += 1
				continue
			}
			//非工作日只需累加加班时长
			if !info.IsWorkDay && info.IsOverTime { //对申请加班的数据进行累加
				data.OTHours += info.OvertimeHour
				continue
			}

			if info.IsNormal {
				if info.IsVacation || info.IsVacationAM || info.IsVacationPM {
					data.ExceptionDays += 1
					continue
				}
				if info.IsWorkDay {
					data.ActualWorkDays += 1
				}
			} else {
				if info.IsNormalAM && info.IsVacationAM {
					data.ExceptionDays += 1
					continue
				}
				if info.IsNormalPM && info.IsVacationPM {
					data.ExceptionDays += 1
					continue
				}
				if info.IsVacation {
					data.VacationDays += 1
					continue
				}
				if info.IsVacationAM {
					data.VacationDays += 0.5
					if info.IsNormalPM {
						data.ActualWorkDays += 0.5
					} else {
						if info.IsLatePM {
							data.LateTimes += 1
						}
						if info.IsLeaveEarlyPM {
							data.LeaveEarlyTimes += 1
						}
						if info.IsAbsentPM {
							data.AbsentTimes += 1
						}
					}
					continue
				}

				if info.IsVacationPM {
					data.VacationDays += 0.5
					if info.IsNormalAM {
						data.ActualWorkDays += 0.5
					} else {
						if info.IsLateAM {
							data.LateTimes += 1
						}
						if info.IsLeaveEarlyAM {
							data.LeaveEarlyTimes += 1
						}
						if info.IsAbsentAM {
							data.AbsentTimes += 1
						}
					}
					continue
				}

				if info.IsLate || info.IsLeaveEarly {
					data.ActualWorkDays += 1
					if info.IsLate {
						data.LateTimes += 1
					}
					if info.IsLeaveEarly {
						data.LeaveEarlyTimes += 1
					}
				}
				if info.IsAbsent {
					data.AbsentTimes += 1
				}

			}
		}
		data.OTDays = data.OTHours / 8.0
		data.ActualWorkDays += data.OTDays
		attend_info_map[id] = data
	}
	return attend_info_map
}

func WriteDataToExcel(start_date, end_date string, attend_data_map map[int64]*entity.AttendanceData, employee_map map[int64]*entity.Employee, dep_map map[int64]*entity.Department) (string, error) {
	xlsx := excelize.NewFile()
	sheet_name := "考勤数据汇总"
	sheet_idx := xlsx.GetActiveSheetIndex()
	old_sheet_name := xlsx.GetSheetName(sheet_idx)
	xlsx.SetSheetName(old_sheet_name, sheet_name)

	xlsx.SetColWidth(sheet_name, "A", "O", 15)
	//title
	xlsx.SetCellStr(sheet_name, "A1", "序号")
	xlsx.SetCellStr(sheet_name, "B1", "姓名")
	xlsx.SetCellStr(sheet_name, "C1", "部门")
	xlsx.SetCellStr(sheet_name, "D1", "所属公司")
	xlsx.SetCellStr(sheet_name, "E1", "实际工作天数")
	xlsx.SetCellStr(sheet_name, "F1", "当月应工作天数")
	xlsx.SetCellStr(sheet_name, "G1", "迟到/未/漏考勤次数")
	xlsx.SetCellStr(sheet_name, "H1", "周末工作小时数")
	xlsx.SetCellStr(sheet_name, "I1", "周末工作天数")
	xlsx.SetCellStr(sheet_name, "J1", "休假天数")
	xlsx.SetCellStr(sheet_name, "K1", "漏考勤次数")
	xlsx.SetCellStr(sheet_name, "L1", "迟到次数")
	xlsx.SetCellStr(sheet_name, "M1", "早退次数")
	xlsx.SetCellStr(sheet_name, "N1", "无考勤次数")
	xlsx.SetCellStr(sheet_name, "O1", "异常天数")

	idx := 1
	row := 2
	for id, data := range attend_data_map {
		info, ok := employee_map[id]
		if !ok {
			continue
		}
		dep_info, ok := dep_map[info.DeptId]
		if !ok {
			continue
		}
		axisA := "A" + strconv.Itoa(row)
		axisB := "B" + strconv.Itoa(row)
		axisC := "C" + strconv.Itoa(row)
		axisD := "D" + strconv.Itoa(row)
		axisE := "E" + strconv.Itoa(row)
		axisF := "F" + strconv.Itoa(row)
		axisG := "G" + strconv.Itoa(row)
		axisH := "H" + strconv.Itoa(row)
		axisI := "I" + strconv.Itoa(row)
		axisJ := "J" + strconv.Itoa(row)
		axisK := "K" + strconv.Itoa(row)
		axisL := "L" + strconv.Itoa(row)
		axisM := "M" + strconv.Itoa(row)
		axisN := "N" + strconv.Itoa(row)
		axisO := "O" + strconv.Itoa(row)

		//xlsx.SetCellInt("Sheet1", axisA, opinion.Id)
		xlsx.SetCellStr(sheet_name, axisA, strconv.Itoa(idx))
		xlsx.SetCellStr(sheet_name, axisB, data.Name)
		xlsx.SetCellStr(sheet_name, axisC, dep_info.DeptName)
		xlsx.SetCellStr(sheet_name, axisD, info.Vendor)
		xlsx.SetCellStr(sheet_name, axisE, strconv.FormatFloat(data.ActualWorkDays, 'f', 1, 64))
		xlsx.SetCellStr(sheet_name, axisF, strconv.Itoa(data.WorkDays))
		xlsx.SetCellStr(sheet_name, axisG, strconv.Itoa(data.LateTimes+data.LeaveEarlyTimes+data.AbsentTimes))
		xlsx.SetCellStr(sheet_name, axisH, strconv.FormatFloat(data.OTHours, 'f', 1, 64))
		xlsx.SetCellStr(sheet_name, axisI, strconv.FormatFloat(data.OTDays, 'f', 1, 64))
		xlsx.SetCellStr(sheet_name, axisJ, strconv.FormatFloat(data.VacationDays, 'f', 1, 64))
		xlsx.SetCellStr(sheet_name, axisK, strconv.Itoa(data.AbsentTimes))
		xlsx.SetCellStr(sheet_name, axisL, strconv.Itoa(data.LateTimes))
		xlsx.SetCellStr(sheet_name, axisM, strconv.Itoa(data.LeaveEarlyTimes))
		xlsx.SetCellStr(sheet_name, axisN, strconv.Itoa(data.LackTimes))
		xlsx.SetCellStr(sheet_name, axisO, strconv.Itoa(data.ExceptionDays))
		idx += 1
		row += 1
	}
	curr_time := time.Now().Format("20060102_15-04-05")
	file_name := config.Server().OutputDir + string(os.PathSeparator) + "考勤数据汇总_" + start_date + "_" + end_date + "_" + curr_time + ".xlsx"
	err := xlsx.SaveAs(file_name)
	if err != nil {
		global.Log.Error("save attendance xlsx failed.", zap.Any("error", err))
		return file_name, err
	}
	return file_name, nil
}

func SendEMail(mailAddress, file_name, start_date, end_date string) error {
	global.Log.Info("sendEMail() start")
	if len(file_name) <= 0 {
		return errors.New("No find attach files.")
	}
	if len(mailAddress) <= 0 {
		return errors.New("Unspecified email address")
	}

	m := gomail.NewMessage()
	// 这种方式可以添加别名，即 nickname， 也可以直接用<code>m.SetHeader("From", MAIL_USER)</code>
	nickname := "dfitc hr"

	m.SetHeader("From", nickname+"<"+config.EMailSetting().Mailuser+">")
	// 发送给多个用户
	m.SetHeader("To", mailAddress)
	// 设置邮件主题
	subject := "外包人员考勤数据汇总_" + start_date + "_" + end_date
	m.SetHeader("Subject", subject)
	// 设置邮件正文
	body := "本次考勤数据的时间区间为" + start_date + "至" + end_date + ", 详见附件"
	m.SetBody("text/html", body)

	m.Attach(file_name)

	d := gomail.NewDialer(config.EMailSetting().Mailhost, config.EMailSetting().Mailport,
		config.EMailSetting().Mailuser, config.EMailSetting().Mailpwd)

	d.SSL = true
	// 发送邮件
	err := d.DialAndSend(m)
	if err != nil {
		global.Log.Error("sendGoMail() failed,error:=", zap.Any("error", err))
	} else {
		global.Log.Info("sendGoMail() finished,success")
	}
	tmp_err := os.Remove(file_name)
	if tmp_err != nil {
		global.Log.Error("Del File "+file_name+" failed,error:=", zap.Any("error", tmp_err))
	}
	return err
}
